#include "Model.h"
#include "../common.h"
#include "../log.h"
#include <math.h>

namespace Tmpl {

Model::Model(ShaderProgram* shader) : mVBO(0), mVBOIndices(0), mVAB(0), mShader(shader), mIndicesCount(0), mRenderMode(GL_TRIANGLES)
{
}
Model::~Model()
{
	if (mVBO) glDeleteBuffers(1, &mVBO);
	if (mVBOIndices) glDeleteBuffers(1, &mVBOIndices);
	if (mVAB) glDeleteBuffers(1, &mVAB);
}
void Model::BuildModel(const VertexArray& vertices, const IndicesArray& indices)
{
	// Generate the VAB
	glGenVertexArrays(1, &mVAB);
	glBindVertexArray(mVAB);

	// Generate Buffer Object
	glGenBuffers(1, &mBufferObjectId);
	glBindBuffer(GL_UNIFORM_BUFFER, mBufferObjectId);
	GLuint uniformBlockIndex = mShader->GetUniformBlockIndex("matModelViews");
	GLint uniformBlockSize;
	mShader->GetActiveUniformBlockiv(uniformBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &uniformBlockSize);
	glBufferData(GL_UNIFORM_BUFFER, uniformBlockSize, NULL, GL_STREAM_DRAW);
	glBindBufferBase(GL_UNIFORM_BUFFER, 0, mBufferObjectId);
	mShader->UniformBlockBinding(uniformBlockIndex, 0);

	//glGetUniformIndices(prog_id, 1, &names[2], &uindex);
	//glGetActiveUniformsiv(prog_id, 1, &uindex, GL_UNIFORM_OFFSET, &offset);
	//glGetActiveUniformsiv(prog_id, 1, &uindex, GL_UNIFORM_SIZE, &singleSize);

	// Create the VBO
	glGenBuffers(1, &mVBO);
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);
	glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);

	// Set the Pointers
	GLint vertexLoc = mShader->GetAttribLoc("InVertex");
	GLint normalLoc = mShader->GetAttribLoc("InNormal");
	GLint texCoordLoc = mShader->GetAttribLoc("InTexCoord");
	
	glEnableVertexAttribArray(vertexLoc);
	glEnableVertexAttribArray(normalLoc);
	glEnableVertexAttribArray(texCoordLoc);

	glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
	glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(float)*3));
	glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(float)*6));

	// Generate Indices VBO
	mVertexCount = vertices.size();
	mIndicesCount = indices.size();
	if (mIndicesCount > 0)
	{
		glGenBuffers(1, &mVBOIndices);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBOIndices);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
	}

	// were done
	glBindVertexArray(0);
}
void Model::Render(const Matrix4x4f& projection, const Matrix4x4f* modelView, int count)
{
	//glEnable(GL_CULL_FACE);

	//glCullFace(GL_BACK);
	//glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

	mShader->Use();
	mShader->UniformMatrix4x4("matProjection", projection);

	BindSettings();

	glBindVertexArray(mVAB);
	while (count > 0)
	{
		int renderCount = Min(count, 256);
		glBufferData(GL_UNIFORM_BUFFER, renderCount * sizeof(Matrix4x4f), &modelView[count-renderCount], GL_STREAM_DRAW);
		if (mIndicesCount > 0)
			glDrawElementsInstanced(mRenderMode, mIndicesCount, GL_UNSIGNED_INT, 0, renderCount);
		else
			glDrawArraysInstanced(mRenderMode, 0, mVertexCount, renderCount);  
		count -= renderCount;
	}
	glBindVertexArray(0);

	UnbindSettings();

	mShader->UnUse();

	//glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

	//glDisable(GL_CULL_FACE);

}

}